﻿using System;
using System.Linq.Expressions;

namespace Alex.Library.Specifications
{
    /// <summary>
    /// specification's base
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Specification<T> : ISpecification<T>
    {
        /// <summary>
        /// get a default specification(ExpressionSpecification)
        /// </summary>
        /// <param name="expression">lambda expression</param>
        /// <returns>default specification</returns>
        public static ISpecification<T> Eval(Expression<Func<T, bool>> expression)
        {
            return new ExpressionSpecification<T>(expression);
        }

        //protected Expression<Func<T, bool>> expression;

        /// <summary>
        /// get the lambda expression
        /// </summary>
        /// <returns></returns>
        public abstract Expression<Func<T, bool>> GetExpression();

        /// <summary>
        /// check the entity satisfy specification or not.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public virtual bool IsSatisfiedBy(T entity)
        {
            //return this.expression.Compile()(entity);
            return this.GetExpression().Compile()(entity);
        }

        /// <summary>
        /// get a specification when And other.
        /// </summary>
        /// <param name="other">any specification</param>
        /// <returns>AndSpecification</returns>
        public virtual ISpecification<T> And(ISpecification<T> other)
        {
            return new AndSpecification<T>(this, other);
        }

        /// <summary>
        /// get a specification when Or other.
        /// </summary>
        /// <param name="other">any specification</param>
        /// <returns>OrSpecification</returns>
        public virtual ISpecification<T> Or(ISpecification<T> other)
        {
            return new OrSpecification<T>(this, other);
        }

        /// <summary>
        /// get a specification when Not.
        /// </summary>        
        /// <returns>NotSpecification</returns>
        public virtual ISpecification<T> Not()
        {
            return new NotSpecification<T>(this);
        }

        /// <summary>
        /// get a specification when And not(other).
        /// </summary>
        /// <param name="other">any specification</param>
        /// <returns>AndNotSpecification</returns>
        public virtual ISpecification<T> AndNot(ISpecification<T> other)
        {
            return new AndNotSpecification<T>(this, other);
        }

        /// <summary>
        /// get a specification when Or not(other).
        /// </summary>
        /// <param name="other">any specification</param>
        /// <returns>OrNotSpecification</returns>
        public virtual ISpecification<T> OrNot(ISpecification<T> other)
        {
            return new OrNotSpecification<T>(this, other);
        }
    }    
}
